# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import copy, sys

class Visitor:
    def defaultVisit(self, node):
        raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
            node.__class__.__name__)

    def visitWhitespace(self, ws):
        pass

    def visitFile(self, f):
        for thing in f.stuff:
            thing.accept(self)

    def visitCppDirective(self, ppd):
        pass

    def visitBlock(self, block):
        for stmt in block.stmts:
            stmt.accept(self)

    def visitNamespace(self, ns):
        self.visitBlock(ns)

    def visitType(self, type):
        pass

    def visitTypeArray(self, ta):
        ta.basetype.accept(self)
        ta.nmemb.accept(self)

    def visitTypeEnum(self, enum):
        pass

    def visitTypeUnion(self, union):
        for t, name in union.components:
            t.accept(self)

    def visitTypedef(self, tdef):
        tdef.fromtype.accept(self)

    def visitUsing(self, us):
        us.type.accept(self)

    def visitForwardDecl(self, fd):
        pass

    def visitDecl(self, decl):
        decl.type.accept(self)

    def visitParam(self, param):
        self.visitDecl(param)
        if param.default is not None:
            param.default.accept(self)

    def visitClass(self, cls):
        for inherit in cls.inherits:
            inherit.accept(self)
        self.visitBlock(cls)

    def visitInherit(self, inh):
        pass

    def visitFriendClassDecl(self, fcd):
        pass

    def visitMethodDecl(self, meth):
        for param in meth.params:
            param.accept(self)
        if meth.ret is not None:
            meth.ret.accept(self)
        if meth.typeop is not None:
            meth.typeop.accept(self)
        if meth.T is not None:
            meth.T.accept(self)

    def visitMethodDefn(self, meth):
        meth.decl.accept(self)
        self.visitBlock(meth)

    def visitFunctionDecl(self, fun):
        self.visitMethodDecl(fun)

    def visitFunctionDefn(self, fd):
        self.visitMethodDefn(fd)

    def visitConstructorDecl(self, ctor):
        self.visitMethodDecl(ctor)

    def visitConstructorDefn(self, cd):
        cd.decl.accept(self)
        for init in cd.memberinits:
            init.accept(self)
        self.visitBlock(cd)

    def visitDestructorDecl(self, dtor):
        self.visitMethodDecl(dtor)

    def visitDestructorDefn(self, dd):
        dd.decl.accept(self)
        self.visitBlock(dd)

    def visitExprLiteral(self, l):
        pass

    def visitExprVar(self, v):
        pass

    def visitExprPrefixUnop(self, e):
        e.expr.accept(self)

    def visitExprBinary(self, e):
        e.left.accept(self)
        e.right.accept(self)

    def visitExprConditional(self, c):
        c.cond.accept(self)
        c.ife.accept(self)
        c.elsee.accept(self)

    def visitExprAddrOf(self, eao):
        self.visitExprPrefixUnop(eao)

    def visitExprDeref(self, ed):
        self.visitExprPrefixUnop(ed)

    def visitExprNot(self, en):
        self.visitExprPrefixUnop(en)

    def visitExprCast(self, ec):
        ec.expr.accept(self)

    def visitExprIndex(self, ei):
        ei.arr.accept(self)
        ei.idx.accept(self)

    def visitExprSelect(self, es):
        es.obj.accept(self)

    def visitExprAssn(self, ea):
        ea.lhs.accept(self)
        ea.rhs.accept(self)

    def visitExprCall(self, ec):
        ec.func.accept(self)
        for arg in ec.args:
            arg.accept(self)

    def visitExprNew(self, en):
        en.ctype.accept(self)
        if en.newargs is not None:
            for arg in en.newargs:
                arg.accept(self)
        if en.args is not None:
            for arg in en.args:
                arg.accept(self)

    def visitExprDelete(self, ed):
        ed.obj.accept(self)

    def visitExprMemberInit(self, minit):
        self.visitExprCall(minit)

    def visitExprSizeof(self, es):
        self.visitExprCall(es)

    def visitStmtBlock(self, sb):
        self.visitBlock(sb)

    def visitStmtDecl(self, sd):
        sd.decl.accept(self)
        if sd.init is not None:
            sd.init.accept(self)

    def visitLabel(self, label):
        pass

    def visitCaseLabel(self, case):
        pass

    def visitDefaultLabel(self, dl):
        pass

    def visitStmtIf(self, si):
        si.cond.accept(self)
        si.ifb.accept(self)
        if si.elseb is not None:
            si.elseb.accept(self)

    def visitStmtFor(self, sf):
        if sf.init is not None:
            sf.init.accept(self)
        if sf.cond is not None:
            sf.cond.accept(self)
        if sf.update is not None:
            sf.update.accept(self)

    def visitStmtSwitch(self, ss):
        ss.expr.accept(self)
        self.visitBlock(ss)

    def visitStmtBreak(self, sb):
        pass

    def visitStmtExpr(self, se):
        se.expr.accept(self)

    def visitStmtReturn(self, sr):
        if sr.expr is not None:
            sr.expr.accept(self)

##------------------------------
class Node:
    def __init__(self):
        pass

    def accept(self, visitor):
        visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
        if visit is None:
            return getattr(visitor, 'defaultVisit')(self)
        return visit(self)

class Whitespace(Node):
    # yes, this is silly.  but we need to stick comments in the
    # generated code without resorting to more serious hacks
    def __init__(self, ws, indent=0):
        Node.__init__(self)
        self.ws = ws
        self.indent = indent
Whitespace.NL = Whitespace('\n')

class File(Node):
    def __init__(self, filename):
        Node.__init__(self)
        self.name = filename
        # array of stuff in the file --- stmts and preprocessor thingies
        self.stuff = [ ]

    def addthing(self, thing):
        assert thing is not None
        assert not isinstance(thing, list)
        self.stuff.append(thing)

    def addthings(self, things):
        for t in things:  self.addthing(t)

    # "look like" a Block so code doesn't have to care whether they're
    # in global scope or not
    def addstmt(self, stmt):
        assert stmt is not None
        assert not isinstance(stmt, list)
        self.stuff.append(stmt)

    def addstmts(self, stmts):
        for s in stmts:  self.addstmt(s)

class CppDirective(Node):
    '''represents |#[directive] [rest]|, where |rest| is any string'''
    def __init__(self, directive, rest=None):
        Node.__init__(self)
        self.directive = directive
        self.rest = rest

class Block(Node):
    def __init__(self):
        Node.__init__(self)
        self.stmts = [ ]

    def addstmt(self, stmt):
        assert stmt is not None
        assert not isinstance(stmt, tuple)
        self.stmts.append(stmt)

    def addstmts(self, stmts):
        for s in stmts:  self.addstmt(s)

##------------------------------
# type and decl thingies
class Namespace(Block):
    def __init__(self, name):
        assert isinstance(name, str)

        Block.__init__(self)
        self.name = name

class Type(Node):
    def __init__(self, name, const=0,
                 ptr=0, ptrconst=0, ptrptr=0, ptrconstptr=0,
                 ref=0,
                 hasimplicitcopyctor=True,
                 T=None):
        """
To avoid getting fancy with recursive types, we limit the kinds
of pointer types that can be be constructed.

  ptr            => T*
  ptrconst       => T* const
  ptrptr         => T**
  ptrconstptr    => T* const*

Any type, naked or pointer, can be const (const T) or ref (T&).
"""
        assert isinstance(name, str)
        assert not isinstance(const, str)
        assert not isinstance(T, str)

        Node.__init__(self)
        self.name = name
        self.const = const
        self.ptr = ptr
        self.ptrconst = ptrconst
        self.ptrptr = ptrptr
        self.ptrconstptr = ptrconstptr
        self.ref = ref
        self.hasimplicitcopyctor = hasimplicitcopyctor
        self.T = T
        # XXX could get serious here with recursive types, but shouldn't 
        # need that for this codegen
    def __deepcopy__(self, memo):
        return Type(self.name,
                    const=self.const,
                    ptr=self.ptr, ptrconst=self.ptrconst,
                    ptrptr=self.ptrptr, ptrconstptr=self.ptrconstptr,
                    ref=self.ref,
                    T=copy.deepcopy(self.T, memo))
Type.BOOL = Type('bool')
Type.INT = Type('int')
Type.INT32 = Type('int32_t')
Type.INTPTR = Type('intptr_t')
Type.NSRESULT = Type('nsresult')
Type.UINT32 = Type('uint32_t')
Type.UINT32PTR = Type('uint32_t', ptr=1)
Type.SIZE = Type('size_t')
Type.VOID = Type('void')
Type.VOIDPTR = Type('void', ptr=1)
Type.AUTO = Type('auto')

class TypeArray(Node):
    def __init__(self, basetype, nmemb):
        '''the type |basetype DECLNAME[nmemb]|.  |nmemb| is an Expr'''
        self.basetype = basetype
        self.nmemb = nmemb
    def __deepcopy__(self, memo):
        return TypeArray(deepcopy(self.basetype, memo), nmemb)

class TypeEnum(Node):
    def __init__(self, name=None):
        '''name can be None'''
        Node.__init__(self)
        self.name = name
        self.idnums = [ ]    # pairs of ('Foo', [num]) or ('Foo', None)

    def addId(self, id, num=None):
        self.idnums.append((id, num))

class TypeUnion(Node):
    def __init__(self, name=None):
        Node.__init__(self)
        self.name = name
        self.components = [ ]           # [ Decl ]

    def addComponent(self, type, name):
        self.components.append(Decl(type, name))

class Typedef(Node):
    def __init__(self, fromtype, totypename, templateargs=[]):
        assert isinstance(totypename, str)
        
        Node.__init__(self)
        self.fromtype = fromtype
        self.totypename = totypename
        self.templateargs = templateargs

    def __cmp__(self, o):
        return cmp(self.totypename, o.totypename)
    def __eq__(self, o):
        return (self.__class__ == o.__class__
                and 0 == cmp(self, o))
    def __hash__(self):
        return hash(self.totypename)

class Using(Node):
    def __init__(self, type):
        Node.__init__(self)
        self.type = type

class ForwardDecl(Node):
    def __init__(self, pqname, cls=0, struct=0):
        assert (not cls and struct) or (cls and not struct)

        self.pqname = pqname
        self.cls = cls
        self.struct = struct

class Decl(Node):
    '''represents |Foo bar|, e.g. in a function signature'''
    def __init__(self, type, name):
        assert type is not None
        assert not isinstance(type, str)
        assert isinstance(name, str)

        Node.__init__(self)
        self.type = type
        self.name = name
    def __deepcopy__(self, memo):
        return Decl(copy.deepcopy(self.type, memo), self.name)

class Param(Decl):
    def __init__(self, type, name, default=None):
        Decl.__init__(self, type, name)
        self.default = default
    def __deepcopy__(self, memo):
        return Param(copy.deepcopy(self.type, memo), self.name,
                     copy.deepcopy(self.default, memo))

##------------------------------
# class stuff
class Class(Block):
    def __init__(self, name, inherits=[ ],
                 interface=0, abstract=0, final=0,
                 specializes=None, struct=0):
        assert not (interface and abstract)
        assert not (abstract and final)
        assert not (interface and final)
        assert not (inherits and specializes)

        Block.__init__(self)
        self.name = name
        self.inherits = inherits        # [ Type ]
        self.interface = interface      # bool
        self.abstract = abstract        # bool
        self.final = final              # bool
        self.specializes = specializes  # Type or None
        self.struct = struct            # bool

class Inherit(Node):
    def __init__(self, type, viz='public'):
        assert isinstance(viz, str)
        Node.__init__(self)
        self.type = type
        self.viz = viz

class FriendClassDecl(Node):
    def __init__(self, friend):
        Node.__init__(self)
        self.friend = friend

class MethodDecl(Node):
    def __init__(self, name, params=[ ], ret=Type('void'),
                 virtual=0, const=0, pure=0, static=0, warn_unused=0,
                 inline=0, force_inline=0, never_inline=0,
                 typeop=None,
                 T=None):
        assert not (virtual and static)
        assert not pure or virtual      # pure => virtual
        assert not (static and typeop)
        assert not (name and typeop)
        assert name is None or isinstance(name, str)
        assert not isinstance(ret, list)
        for decl in params:  assert not isinstance(decl, str)
        assert not isinstance(T, int)
        assert not (inline and never_inline)
        assert not (force_inline and never_inline)

        if typeop is not None:
            ret = None

        Node.__init__(self)
        self.name = name
        self.params = params            # [ Param ]
        self.ret = ret                  # Type or None
        self.virtual = virtual          # bool
        self.const = const              # bool
        self.pure = pure                # bool
        self.static = static            # bool
        self.warn_unused = warn_unused  # bool
        self.force_inline = (force_inline or T) # bool
        self.inline = inline            # bool
        self.never_inline = never_inline # bool
        self.typeop = typeop            # Type or None
        self.T = T                      # Type or None
        self.only_for_definition = False

    def __deepcopy__(self, memo):
        return MethodDecl(
            self.name,
            params=copy.deepcopy(self.params, memo),
            ret=copy.deepcopy(self.ret, memo),
            virtual=self.virtual,
            const=self.const,
            pure=self.pure,
            static=self.static,
            warn_unused=self.warn_unused,
            inline=self.inline,
            force_inline=self.force_inline,
            never_inline=self.never_inline,
            typeop=copy.deepcopy(self.typeop, memo),
            T=copy.deepcopy(self.T, memo))

class MethodDefn(Block):
    def __init__(self, decl):
        Block.__init__(self)
        self.decl = decl

class FunctionDecl(MethodDecl):
    def __init__(self, name, params=[ ], ret=Type('void'),
                 static=0, warn_unused=0,
                 inline=0, force_inline=0,
                 T=None):
        MethodDecl.__init__(self, name, params=params, ret=ret,
                            static=static, warn_unused=warn_unused,
                            inline=inline, force_inline=force_inline,
                            T=T)

class FunctionDefn(MethodDefn):
    def __init__(self, decl):
        MethodDefn.__init__(self, decl)

class ConstructorDecl(MethodDecl):
    def __init__(self, name, params=[ ], explicit=0, force_inline=0):
        MethodDecl.__init__(self, name, params=params, ret=None,
                            force_inline=force_inline)
        self.explicit = explicit

    def __deepcopy__(self, memo):
        return ConstructorDecl(self.name,
                               copy.deepcopy(self.params, memo),
                               self.explicit)

class ConstructorDefn(MethodDefn):
    def __init__(self, decl, memberinits=[ ]):
        MethodDefn.__init__(self, decl)
        self.memberinits = memberinits

class DestructorDecl(MethodDecl):
    def __init__(self, name, virtual=0, force_inline=0, inline=0):
        MethodDecl.__init__(self, name, params=[ ], ret=None,
                            virtual=virtual,
                            force_inline=force_inline, inline=inline)

    def __deepcopy__(self, memo):
        return DestructorDecl(self.name,
                              virtual=self.virtual,
                              force_inline=self.force_inline,
                              inline=self.inline)

        
class DestructorDefn(MethodDefn):
    def __init__(self, decl):  MethodDefn.__init__(self, decl)

##------------------------------
# expressions
class ExprVar(Node):
    def __init__(self, name):
        assert isinstance(name, str)
        
        Node.__init__(self)
        self.name = name
ExprVar.THIS = ExprVar('this')

class ExprLiteral(Node):
    def __init__(self, value, type):
        '''|type| is a Python format specifier; 'd' for example'''
        Node.__init__(self)
        self.value = value
        self.type = type

    @staticmethod
    def Int(i):  return ExprLiteral(i, 'd')

    @staticmethod
    def String(s):  return ExprLiteral('"'+ s +'"', 's')

    @staticmethod
    def WString(s):  return ExprLiteral('L"'+ s +'"', 's')

    def __str__(self):
        return ('%'+ self.type)% (self.value)
ExprLiteral.ZERO = ExprLiteral.Int(0)
ExprLiteral.ONE = ExprLiteral.Int(1)
ExprLiteral.NULL = ExprVar('nullptr')
ExprLiteral.TRUE = ExprVar('true')
ExprLiteral.FALSE = ExprVar('false')

class ExprPrefixUnop(Node):
    def __init__(self, expr, op):
        assert not isinstance(expr, tuple)
        self.expr = expr
        self.op = op

class ExprNot(ExprPrefixUnop):
    def __init__(self, expr):
        ExprPrefixUnop.__init__(self, expr, '!')

class ExprAddrOf(ExprPrefixUnop):
    def __init__(self, expr):
        ExprPrefixUnop.__init__(self, expr, '&')

class ExprDeref(ExprPrefixUnop):
    def __init__(self, expr):
        ExprPrefixUnop.__init__(self, expr, '*')

class ExprCast(Node):
    def __init__(self, expr, type,
                 dynamic=0, static=0, reinterpret=0, const=0, C=0):
        assert 1 == reduce(lambda a, x: a+x, [ dynamic, static, reinterpret, const, C ])

        Node.__init__(self)
        self.expr = expr
        self.type = type
        self.dynamic = dynamic
        self.static = static
        self.reinterpret = reinterpret
        self.const = const
        self.C = C

class ExprBinary(Node):
    def __init__(self, left, op, right):
        Node.__init__(self)
        self.left = left
        self.op = op
        self.right = right

class ExprConditional(Node):
    def __init__(self, cond, ife, elsee):
        Node.__init__(self)
        self.cond = cond
        self.ife = ife
        self.elsee = elsee

class ExprIndex(Node):
    def __init__(self, arr, idx):
        Node.__init__(self)
        self.arr = arr
        self.idx = idx

class ExprSelect(Node):
    def __init__(self, obj, op, field):
        assert obj and op and field
        assert not isinstance(obj, str)
        assert isinstance(field, str)
        
        Node.__init__(self)
        self.obj = obj
        self.op = op
        self.field = field

class ExprAssn(Node):
    def __init__(self, lhs, rhs, op='='):
        Node.__init__(self)
        self.lhs = lhs
        self.op = op
        self.rhs = rhs

class ExprCall(Node):
    def __init__(self, func, args=[ ]):
        assert hasattr(func, 'accept')
        assert isinstance(args, list)
        for arg in args:  assert arg and not isinstance(arg, str)

        Node.__init__(self)
        self.func = func
        self.args = args

class ExprMove(ExprCall):
    def __init__(self, arg):
        ExprCall.__init__(self, ExprVar("mozilla::Move"), args=[arg])

class ExprNew(Node):
    # XXX taking some poetic license ...
    def __init__(self, ctype, args=[ ], newargs=None):
        assert not (ctype.const or ctype.ref)

        Node.__init__(self)
        self.ctype = ctype
        self.args = args
        self.newargs = newargs

class ExprDelete(Node):
    def __init__(self, obj):
        Node.__init__(self)
        self.obj = obj

class ExprMemberInit(ExprCall):
    def __init__(self, member, args=[ ]):
        ExprCall.__init__(self, member, args)

class ExprSizeof(ExprCall):
    def __init__(self, t):
        ExprCall.__init__(self, ExprVar('sizeof'), [ t ])

##------------------------------
# statements etc.
class StmtBlock(Block):
    def __init__(self, stmts=[ ]):
        Block.__init__(self)
        self.addstmts(stmts)

class StmtDecl(Node):
    def __init__(self, decl, init=None, initargs=None):
        assert not (init and initargs)
        assert not isinstance(init, str) # easy to confuse with Decl
        assert not isinstance(init, list)
        assert not isinstance(decl, tuple)
        
        Node.__init__(self)
        self.decl = decl
        self.init = init
        self.initargs = initargs

class Label(Node):
    def __init__(self, name):
        Node.__init__(self)
        self.name = name
Label.PUBLIC = Label('public')
Label.PROTECTED = Label('protected')
Label.PRIVATE = Label('private')

class CaseLabel(Node):
    def __init__(self, name):
        Node.__init__(self)
        self.name = name

class DefaultLabel(Node):
    def __init__(self):
        Node.__init__(self)

class StmtIf(Node):
    def __init__(self, cond):
        Node.__init__(self)
        self.cond = cond
        self.ifb = Block()
        self.elseb = None

    def addifstmt(self, stmt):
        self.ifb.addstmt(stmt)

    def addifstmts(self, stmts):
        self.ifb.addstmts(stmts)
        
    def addelsestmt(self, stmt):
        if self.elseb is None: self.elseb = Block()
        self.elseb.addstmt(stmt)

    def addelsestmts(self, stmts):
        if self.elseb is None: self.elseb = Block()
        self.elseb.addstmts(stmts)

class StmtFor(Block):
    def __init__(self, init=None, cond=None, update=None):
        Block.__init__(self)
        self.init = init
        self.cond = cond
        self.update = update

class StmtRangedFor(Block):
    def __init__(self, var, iteree):
        assert isinstance(var, ExprVar)
        assert iteree

        Block.__init__(self)
        self.var = var
        self.iteree = iteree

class StmtSwitch(Block):
    def __init__(self, expr):
        Block.__init__(self)
        self.expr = expr
        self.nr_cases = 0

    def addcase(self, case, block):
        '''NOTE: |case| is not checked for uniqueness'''
        assert not isinstance(case, str)
        assert (isinstance(block, StmtBreak)
                or isinstance(block, StmtReturn)
                or isinstance(block, StmtSwitch)
                or (hasattr(block, 'stmts')
                    and (isinstance(block.stmts[-1], StmtBreak)
                         or isinstance(block.stmts[-1], StmtReturn))))
        self.addstmt(case)
        self.addstmt(block)
        self.nr_cases += 1

    def addfallthrough(self, case):
        self.addstmt(case)
        self.nr_cases += 1

class StmtBreak(Node):
    def __init__(self):
        Node.__init__(self)

class StmtExpr(Node):
    def __init__(self, expr):
        assert expr is not None
        
        Node.__init__(self)
        self.expr = expr

class StmtReturn(Node):
    def __init__(self, expr=None):
        Node.__init__(self)
        self.expr = expr

StmtReturn.TRUE = StmtReturn(ExprLiteral.TRUE)
StmtReturn.FALSE = StmtReturn(ExprLiteral.FALSE)
